package com.panopset.sf;

import java.awt.Image;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.swing.JComponent;

import com.panopset.compat.Logz;
import com.panopset.compat.Strings;
import com.panopset.compat.Zombie;
import com.panopset.io.AppBase;
import com.panopset.io.AppProps;
import com.panopset.io.Filez;
import com.panopset.sf.FontManager.Size;
import com.panopset.sf.env.LogHistory;

/**
 * Panopset Swing application framework.
 * 
 * To customize for your organization, set the following System properties prior
 * to instantiating this class:
 * 
 * <table>
 * <tr>
 * <th>System property</th>
 * <th>Default</th>
 * </tr>
 * <tr>
 * <td>com.panopset.org.home</td>
 * <td>com.panopset</td>
 * </tr>
 * <tr>
 * <td>com.panopset.org.favicon</td>
 * <td>/com/panopset/site/favicon.png</td>
 * </tr>
 * </table>
 * 
 * Alternatively you may call setOrgHome and setIconResourcePath, prior to
 * launch, to accomplish the same thing.
 * 
 * @author Karl Dinwiddie
 * 
 */
public abstract class SwingFrame {

    private LogHistory logHistory;

    public LogHistory getLogHistory() {
        if (logHistory == null) {
            logHistory = new LogHistory();
            Logz.addListener(logHistory);
        }
        return logHistory;
    }

    /**
     * Initial application launch.
     */
    public final synchronized void show() {
        getLogHistory();
        try {
            FontManager.getInstance().setSize(
                    FontManager.Size.find(Integer.parseInt(getAppProps()
                            .getFileProps().get(FONT_SIZE_KEY))));
        } catch (Throwable t) {
            // nothing to do.
        }
        for (String s : getAppProps().getAppBase().getDocsList()) {
            add(new Windoc(this, new File(s)));
        }
        if (getDocsCopy().isEmpty()) {
            add(new Windoc(this, findNextUntitled()));
        }
    }

    private static final String FONT_SIZE_KEY = "com.panopset.font_size";

    public synchronized void setFontSize(Size size) {
        FontManager.getInstance().setSize(size);
        for (Windoc sd : getDocsCopy()) {
            sd.updateFontMenuCheckboxes(size);
        }
    }

    public AppBase getAppBase() {
        return getAppProps().getAppBase();
    }

    private AppProps appProps;

    private AppProps getAppProps() {
        if (appProps == null) {
            appProps = new AppProps(this.getClass().getSimpleName());
        }
        return appProps;
    }

    /**
     * @param doc
     *            Document to create panel for.
     * @return Application customized main JPanel.
     */
    protected abstract JComponent createPanel(Windoc doc);

    /**
     * Icon resource path.
     */
    private String iconResourcePath;

    /**
     * Override this method to provide your own application frame icons.
     * 
     * @return Icon resource path.
     */
    public String getIconResourcePath() {
        if (iconResourcePath == null) {
            iconResourcePath = System.getProperty("com.panopset.org.favicon");
            if (!(Strings.isPopulated(iconResourcePath))) {
                iconResourcePath = DFT_ICON_PATH;
            }
        }
        return iconResourcePath;
    }

    /**
     * 
     * @param value
     *            Resource path to 16x16 frame icon.
     */
    public void setIconResourcePath(final String value) {
        iconResourcePath = value;
    }

    /**
     * /com/panopset/site/favicon.ico.
     */
    private static final String DFT_ICON_PATH

    = "/com/panopset/site/favicon.png";

    /**
     * 
     * @param value
     *            ie com.company
     */
    public void setOrgHome(final String value) {
        getAppProps().setOrgHome(value);
    }

    public void doNew() {
        add(new Windoc(this, findNextUntitled()));
    }

    protected synchronized int getNumberOfOpenDocs() {
        return getDocsCopy().size();
    }

    private File findNextUntitled() {
        File rtn = null;
        for (int i = 0; i < 100; i++) {
            StringWriter fn = new StringWriter();
            fn.append(new Filez().combinePaths(getAppProps().getAppBase()
                    .getBaseDirectory(), "documents/Untitled"));
            if (i < 10) {
                fn.append("0");
            }
            fn.append("" + i);
            fn.append(".txt");
            rtn = new File(fn.toString());
            if (!isDocOpenForFile(rtn)) {
                return rtn;
            }
        }
        return rtn;
    }

    protected synchronized void add(final Windoc sd) {
        getDocs().add(sd);
        sd.show();
    }

    protected synchronized void saveApplication() {
        try {
        	BufferedWriter bw = new BufferedWriter(new FileWriter(
        
                getAppProps().getAppBase().getAppDocsFile()));
            final String eol = Strings.getEol();
            for (String s : getAppProps().getAppBase().getDocsList()) {
                bw.write(s);
                bw.write(eol);
            }
            bw.flush();
            bw.close();
            
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
        getAppProps().getFileProps().put(FONT_SIZE_KEY,
                "" + FontManager.getInstance().getSize().getValue());
        getAppProps().getFileProps().flush();
    }

    private synchronized void rememberOpenDocs() {
        getAppProps().getAppBase().getDocsList().clear();
        for (Windoc d : getDocsCopy()) {
            if (d.getFrame().isVisible()) {
                getAppProps()
                        .getAppBase()
                        .getDocsList()
                        .add(new Filez().getCanonicalPath(d.getFileProps()
                                .getFile()));
            }
        }
    }

    private synchronized void closeAllDocs() {
        for (Windoc d : getDocsCopy()) {
            if (d.getFrame().isVisible()) {
                getAppProps()
                        .getAppBase()
                        .getDocsList()
                        .add(new Filez().getCanonicalPath(d.getFileProps()
                                .getFile()));
                d.close();
            }
        }
    }

    protected synchronized boolean isDocOpenForFile(File file) {
        Filez fz = new Filez();
        for (Windoc sd : getDocsCopy()) {
            if (fz.getCanonicalPath(file).equals(
                    fz.getCanonicalPath(sd.getFileProps().getFile()))) {
                return true;
            }
        }
        return false;
    }

    private List<Windoc> docs;

    private List<Windoc> getDocs() {
        if (docs == null) {
            docs = Collections.synchronizedList(new ArrayList<Windoc>());
        }
        return docs;
    }

    private synchronized List<Windoc> getDocsCopy() {
        List<Windoc> closedDocs = new ArrayList<Windoc>();
        for (Windoc sd : getDocs()) {
            if (sd.isClosed()) {
                closedDocs.add(sd);
            }
        }
        for (Windoc sd : closedDocs) {
            docs.remove(sd);
        }
        List<Windoc> rtn = new ArrayList<Windoc>();
        for (Windoc sd : docs) {
            rtn.add(sd);
        }
        return rtn;
    }

    /**
     * Show about box.
     */
    public void doAbout() {
        getAboutBox().show();
    }

    /**
     * @return About box.
     */
    public abstract PopupBox createAboutBox();

    /**
     * About box.
     */
    private PopupBox popupBox;

    /**
     * @return about box.
     */
    public final PopupBox getAboutBox() {
        if (popupBox == null) {
            popupBox = createAboutBox();
        }
        return popupBox;
    }

    private Image icon16x16;

    public Image getIcon16x16() {
        if (icon16x16 == null) {
            icon16x16 = getFavicon().getImageResource().getImage();
        }
        return icon16x16;
    }

    private RezIcon favIcon;

    public RezIcon getFavicon() {
        if (favIcon == null) {
            favIcon = new RezIcon(getIconResourcePath());
        }
        return favIcon;
    }

    public synchronized void userCommandExit() {
        rememberOpenDocs();
        closeAllDocs();
        shutdownApplication();
    }

    public synchronized void shutdownApplication() {
        saveApplication();
        Zombie.stop();
    }
}
